﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Microsoft.MSE.Logging
{
    /// <summary>
    /// Logger class that extends the built in trace class functionality included in the System.Diagnostic namespace. 
    /// </summary>
    public sealed class Logger
    {

        #region Fields

        /// <summary>
        /// Name of the class that is being logged by this logger implementation
        /// </summary>
        private readonly string _className;

        /// <summary>
        /// The trace source where logging information will be sent to
        /// </summary>
        private System.Diagnostics.TraceSource _traceSource;

        /// <summary>
        /// Bit mask that will determine which logging levels are active for this logger
        /// </summary>
        private readonly long _logLevelMask;

        #endregion

        #region Constants

        /// <summary>
        /// Constant that holds the standard message text formatting
        /// </summary>
        private const string LOGMESSAGEFORMAT = "<LogEvent ClassName='{0}' EventGuid='{1}'> <EventMessage>{2}</EventMessage> </LogEvent>";

        /// <summary>
        /// Constant that holds the exception message text formatting
        /// </summary>
        private const string LOGEXCEPTIONMESSAGEFORMAT = "<LogEvent ClassName='{0}' EventGuid='{1}'> <EventMessage>{2}</EventMessage> <EventException>{3}</EventException> </LogEvent>";


        #endregion

        #region Constructor

        /// <summary>
        /// Creates an instance of the logger class
        /// </summary>
        /// <param name="className">The name of the class that will be logged</param>
        /// <param name="source">The trace source that will handle logging from this logger</param>
        /// <remarks> the same trace source can be used accross multiple instances of the logger class.</remarks>
        /// <example>
        /// <code>
        ///  static readonly Microsoft.MSE.Logging.Logger _log = new Microsoft.MSE.Logging.Logger(typeof(MockClassToLog), loggingSource);
        /// </code>
        /// </example>
        public Logger(Type className,TraceSource source)
        {
            try
            {
                //Setting the name of the class and the tracing source
                _className = className.FullName;
                _traceSource = source;

                //Setting the logging level this logger supports
                _logLevelMask = DetermineLoggingLevelMask(_traceSource.Switch.Level);

            }
            catch
            {
                //Intentinally swallowing any exceptions that occur during construction 
            }
        }


        #endregion 

        #region Constructor helper classes

        /// <summary>
        /// Helper that determines what logging levels should be logged
        /// </summary>
        /// <param name="loggingLevel">The logging level that is supported for logging</param>
        /// <returns>Bitmask is returned that stores which logging levels are allowed.</returns>
        private static long DetermineLoggingLevelMask(System.Diagnostics.SourceLevels loggingLevel)
        {
            //Variable to hold the logging bit mask
            long loggingLevelMask = 0;


            switch (loggingLevel)
            {
                case SourceLevels.ActivityTracing:
                    loggingLevelMask = (long)TraceEventType.Stop | (long)TraceEventType.Start | (long)TraceEventType.Suspend | (long)TraceEventType.Resume | (long)TraceEventType.Transfer;

                    break;

                case SourceLevels.All:
                    loggingLevelMask = (long)TraceEventType.Critical | (long)TraceEventType.Error | (long)TraceEventType.Information |
                                       (long)TraceEventType.Resume | (long)TraceEventType.Start | (long)TraceEventType.Stop | (long)TraceEventType.Suspend |
                                       (long)TraceEventType.Transfer | (long)TraceEventType.Verbose | (long)TraceEventType.Warning;

                    break;

                case SourceLevels.Critical:
                    loggingLevelMask = (long)TraceEventType.Critical;

                    break;

                case SourceLevels.Error:

                    loggingLevelMask = (long)TraceEventType.Error | (long)TraceEventType.Critical;

                    break;

                case SourceLevels.Information:

                    loggingLevelMask = (long)TraceEventType.Error | (long)TraceEventType.Critical | (long)TraceEventType.Warning | (long)TraceEventType.Information;

                    break;

                case SourceLevels.Off:
                    //Intentinally blank

                    break;
                case SourceLevels.Verbose:

                    loggingLevelMask = (long)TraceEventType.Error | (long)TraceEventType.Critical | (long)TraceEventType.Warning | (long)TraceEventType.Information | (long)TraceEventType.Verbose;

                    break;

                case SourceLevels.Warning:

                    loggingLevelMask = (long)TraceEventType.Error | (long)TraceEventType.Critical | (long)TraceEventType.Warning;

                    break;
                default:

                    //Intentinally blank
                    break;
            }

            return loggingLevelMask;
        }

        #endregion

        #region Logging Methods

        /// <summary>
        /// Logs tracing information into the configured tracing source
        /// </summary>
        /// <param name="level">The tracing event type to be logged. There is no restriction on which trace event type is used.</param>
        /// <param name="message">The information to be logged.</param>
        /// <remarks>This will always set the trace ID to 0</remarks>
        /// <returns>Guid will be returned that identifies this trace event.</returns>
        public Guid Log (System.Diagnostics.TraceEventType level, string message)
        {
            Guid logId = Guid.NewGuid();

            try
            {
                _traceSource.TraceEvent(level, 0, LOGMESSAGEFORMAT, new object[] {_className, logId, message });

            }
            catch
            {
                //Intentinally swallowing any exceptions that occur
            }

            return logId;
        }

        /// <summary>
        /// Logs tracing information into the configured tracing source
        /// </summary>
        /// <param name="level">The tracing event type to be logged. There is no restriction on which trace event type is used.</param>
        /// <param name="message">The information to be logged.</param>
        /// <param name="id">The id number of the trace event</param>
        /// <returns>Guid will be returned that identifies this trace event.</returns>
        public Guid Log(System.Diagnostics.TraceEventType level, string message,int id)
        {
            Guid logId = Guid.NewGuid();

            try
            {
                _traceSource.TraceEvent(level,id, LOGMESSAGEFORMAT, new object[] {_className, logId, message });
            }
            catch
            {
                //Intentinally swallowing any exceptions that occur
            }

            return logId;
        }

        /// <summary>
        /// Logs tracing information and detailed exception information into the configured tracing source
        /// </summary>
        /// <param name="level">The tracing event type to be logged. There is no restriction on which trace event type is used.</param>
        /// <param name="message">The information to be logged.</param>
        /// <param name="exception">The exception to be logged.</param>
        /// <remarks>This will always set the trace ID to 0</remarks>
        /// <returns>Guid will be returned that identifies this trace event.</returns>
        public Guid LogException (System.Diagnostics.TraceEventType level, string message, System.Exception exception)
        {
            Guid logId = Guid.NewGuid();
            try
            {
                _traceSource.TraceEvent(level, 0, LOGEXCEPTIONMESSAGEFORMAT, new object[] {_className, logId, message, exception.ToString() });
            }
            catch
            {
                //intentinally swallowing any exceptions that occur
            }

            return logId;
        }

        /// <summary>
        /// Logs tracing information and detailed exception information into the configured tracing source
        /// </summary>
        /// <param name="level">The tracing event type to be logged. There is no restriction on which trace event type is used.</param>
        /// <param name="message">The information to be logged.</param>
        /// <param name="exception">The exception to be logged.</param>
        /// <param name="id">The id number to be associated with the logged event</param>
        /// <returns>Guid will be returned that identifies this trace event.</returns>
        public Guid LogException(System.Diagnostics.TraceEventType level, string message, System.Exception exception, int id)
        {
            Guid logId = Guid.NewGuid();
            try
            {
                _traceSource.TraceEvent(level, 0, LOGEXCEPTIONMESSAGEFORMAT, new object[] {_className, logId, message, exception.ToString() });
            }
            catch
            {
                //intentinally swallowing any exceptions that occur
            }

            return logId;
        }


        /// <summary>
        /// Determines if the logging level is active.
        /// </summary>
        /// <param name="level">Level to check if active</param>
        /// <remarks>Return value of true means the logging level is active. 
        /// Return value of false means the logging level is not active </remarks>
        /// <returns>Boolean flag that determines if the logging level is active</returns>
        public bool IsLoggingLevelActive(System.Diagnostics.TraceEventType level)
        {
            bool isActive = false;
            long levelValue = (long)level;
            if ((levelValue & _logLevelMask) == levelValue)
            {
                isActive = true;
            }

            return isActive;
        }


        #endregion
    }
}